package de.hpi.generator.config;

import java.util.Hashtable;

public class DistributionModel {

    /**
     * Distribution Model type
     */
    public String type = null;
    /**
     * parameters of a distribution model
     */
    public String params = null;
    /**
     * Will contain all SpecializedDistributionModel objects
     */
    private static Hashtable<String, DistributionModel> aSpecializedDistributionModels = new Hashtable<String, DistributionModel>();

    public DistributionModel() {
    }

    /**
     * Constructs an object with customized parameters
     * 
     * @param type
     *            Distribution Model type
     * @param params
     *            parameters of a distribution model
     */
    public DistributionModel(String type, String params) {
        this.type = type;
        this.params = params;
    }

    /**
     * parse parameters to hashtable with key as parameter's name and value as
     * parameter's value
     * 
     * @return hashtable with key as parameter's name and value as parameter's
     *         value
     */
    public Hashtable<String, String> parseParams() {
        Hashtable<String, String> result = new Hashtable<String, String>();

        // check parameters string for empty
        if (!this.params.isEmpty()) {
            // no. parameters string contains data. process it

            // splits parameters string to parameter's records list by comma
            String aParameterRecords[] = this.params.split(",");
            // split every parameter's record to its parts name and value
            for (String aParameterRecord : aParameterRecords) {
                // split parameter's record to its parts: name of parameter and
                // value of parameter
                String aParameterRecordParts[] = aParameterRecord.split(":");
                // put parameter's parts into result's Hashtable as name of
                // parameter as key and value of parameter as value
                result.put(aParameterRecordParts[0], aParameterRecordParts[1]);
            }
        }
        return result;
    }

    public static Hashtable<String, DistributionModel> getSpecializedDistributionModels() {
        // check aSpecializedDistributionModels initialization
        if (aSpecializedDistributionModels.isEmpty()) {
            // was not initialized, initialize it
            initializeSpecializedDistributionModels();
        }

        return aSpecializedDistributionModels;
    }

    /**
     * initialize aSpecializedDistributionModels with all
     * SpecializedDistributionModel objects
     */
    private static void initializeSpecializedDistributionModels() {
        // register every SpecializedDistributionModel object in the container
        registerSpecializedDistributionModel(new DistributionModelGAUSS());
        registerSpecializedDistributionModel(new DistributionModelPOISSON());
        registerSpecializedDistributionModel(new DistributionModelUNIFORM());
        registerSpecializedDistributionModel(new DistributionModelSEQUENCE());
        registerSpecializedDistributionModel(new DistributionModelZIPF());
        registerSpecializedDistributionModel(new DistributionModelZero());
    }

    /**
     * register a SpecializedDistributionModel object in the container
     * 
     * @param DistributionModel
     *            a SpecializedDistributionModel object
     */
    private static void registerSpecializedDistributionModel(DistributionModel aSpecializedDistributionModel) {
        // put SpecializedDistributionModel object into container with
        // DistributionModel type as key and object as value
        aSpecializedDistributionModels.put(aSpecializedDistributionModel.type, aSpecializedDistributionModel);
    }

    /**
     * create a copy of aSpecializedDistributionModel object with specialized
     * params, stored in this object and returns it
     * 
     * @return copy of aSpecializedDistributionModel object
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public DistributionModel createSpecializedDistributionModel() throws Exception {
        // parse aSpecializedDistributionModel Params
        Hashtable<String, String> aParams = this.parseParams();
        // find aSpecializedDistributionModel for required type in the container
        DistributionModel aSpecializedDistributionModel = getSpecializedDistributionModels().get(this.type);
        // create a copy of aSpecializedDistributionModel object with
        // specialized params and return it
        if(aSpecializedDistributionModel == null){
            throw new Exception(String.format("The specialized distribution model %s was not found", this.type));
        }
        
        DistributionModel result = aSpecializedDistributionModel.createCopyWithSpecializedParams(aParams);
        result.params = this.params;
        return result;
    }

    /**
     * creates a copy of aSpecializedDistributionModel object with specialized
     * params
     * 
     * @param aParams
     *            specialized params
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    protected DistributionModel createCopyWithSpecializedParams(Hashtable<String, String> aParams) throws InstantiationException, IllegalAccessException {
        // creates a copy of aSpecializedDistributionModel object
        DistributionModel result = (DistributionModel) this.getClass().newInstance();
        // setup object with specialized params
        result.setupObjectWithSpecializedParams(aParams);
        return result;
    }

    /**
     * setup object with specialized params. In this base class does nothing,
     * must be overridden
     * 
     * @param aParams
     *            specialized params
     * 
     */
    protected void setupObjectWithSpecializedParams(Hashtable<String, String> aParams) {
        // in this base class does nothing, must be overridden
    }

    @Override
    public String toString() {
        return String.format("{type[%s], params[%s]}\n", this.type, this.params);
    }
}
